/**
 * Copyright 2018 人人开源 http://www.renren.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.tang.common.config;

import com.tang.xss.XssFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.DispatcherType;

/**
 * Filter配置（参考）：
 *   servlet规范中中引入的filter是非常有用的，因为它引入了一个功能强大的拦截模式，
 *   filter是这样的一种java对象，它可以在request到达servlet之前拦截HttpServletRequest对象，
 *   也可以在服务方法转移控制后拦截HttpServletResponse对象
 *   我们可以使用filter对象完成的任务有：检查用户的输入、以及压缩web内容。
 *   但是，当我们在使用filter的时候却会发现至少有一半的时间我们都想改变HttpServletRequest对象的参数。
 *   如：用filter在HttpServletRequest对象到达Servlet之前将用户输入的空格去掉。
 *   但是由于java.util.Map包装的HttpServletRequest对象的参数是不可改变的，那要怎么办呢？
 *   幸运的是，尽管我们不能改变对象本身，但是可以通过装饰模式来改变其状态，
 *   比如在上文中编写的XssHttpServletRequestWrapper就是HttpServletRequest类的装饰类，
 *   该类继承的HttpServletRequestWrapper类是HttpServletRequest类的装饰类
 * @author Mark 15062230055@163.com
 * @since 2.1.0 201804-21
 */
@Configuration
public class FilterConfig {

    /**
     * <p>
     * Shiro的filter必须放在其他filter之前
     * Spring 提供了FilterRegistrationBean类，此类提供setOrder方法，可以为filter设置排序值，
     * 让spring在注册web filter之前排序后再依次注册。
     * 最新spring-boot增加Filter方法，可以参考https://www.jianshu.com/p/05c8be17c80a，即使用Servlet3.0 api中提供的注解 @WebServlet 、@WebListener、@WebFilter
     * SpringBoot提供了三种Bean，分别是FilterRegistrationBean、ServletRegistrationBean、ServletListenerRegistrationBean
     * 分别对应配置原生的Filter、Servlet、Listener
     * <p>
     * @return
     */
    @Bean
    // @Order(Integer.MAX_VALUE) // 它的值为2147483648，MIN_VALUE为-2147483648
    public FilterRegistrationBean shiroFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        // 注册过滤器，DelegatingFilterProxy类存在与spring-web包中，其作用就是一个filter的代理
        // 使filter可以使用spring中的bean，达到可插拔的效果
        // 用DelegatingFilterProxy的好处是可以通过spring容器来管理filter的生命周期
        // 可以通过spring注入的形式，来代理一个filter执行，如shiro
        // DelegatingFilterProxy类继承GenericFilterBean，间接实现了Filter这个接口
        // 故而该类属于一个过滤器。那么就会有实现Filter中init、doFilter、destroy三个方法。
        // 下面的这个配置相当于web.xml配置中的：
        // <filter>
        //   <filter-name>shiroFilter</filter-name>
        //   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        //   <init-param>
        //      <param-name>targetFilterLifecycle</param-name>
        //      <param-value>true</param-value>
        //   </init-param>
        //</filter>
        //<filter-mapping>
        //   <filter-name>shiroFilter</filter-name>
        //   <url-pattern>/xx/*</url-pattern>
        //</filter-mapping>
        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        // 是否启用引入filter的init()和destroy()方法
        // 该值缺省为false，表示生命周期由SpringApplicationContext管理，
        // 设置为true则表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        // 是否自动注册，false为取消Filter的自动注册
        registration.setEnabled(true);
        // 执行过滤顺序，值越小，越先执行，在web.xml中，我们知道，执行顺序是谁在前边执行谁。
        registration.setOrder(Integer.MAX_VALUE - 1);
        registration.addUrlPatterns("/*");
        return registration;
    }

    /**
     * XSS过滤
     * @return
     */
    @Bean
    public FilterRegistrationBean xssFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns("/*");
        registration.setName("xssFilter");
        registration.setOrder(Integer.MAX_VALUE);
        return registration;
    }

    /**
     * SpringBoot在使用SpringMvc的时候不需要配置DispatcherServlet的，因为已经自动配置了
     * 可以通过注入DispatcherServlet 然后用ServletRegistrationBean包裹一层 动态的加上一些初始参数
     * 但是如果想要加一些初始配置参数如何解决，方案如下：
     */
//    @Bean
//    public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
//        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
//        registration.addUrlMappings("*.do");
//        registration.addUrlMappings("*.json");
//        return registration;
//    }
}
